#!/usr/bin/perl
use JSON;
use Getopt::Long;
use Term::ANSIColor;
use Data::Dumper;

my $opt_git_clone        = 0;
my $opt_git_pull         = 0;
my $opt_git_update       = 0;
my $git_options          = "";
my $git_branch_name      = "DeFaulT";
my $git_tag_name         = "DeFaulT";
my @git_cfg_file_list    = ();
my $cfg_file_update_flag = 0;
my $opt_help             = 0;

sub usage() {
    print STDERR << "EOF";
This program is used to resolve dependency at any hierarchy of the IP where 
there is a star_git.cfg file.

usage: $0 [-h] [-f <file_name>] {-c | -p | -b <branch_name> | -t <tag_name> | -g <command>} 

    -h|--help                 : this (help) message
    -f|--file   <file_name>   : Specifies a mb git configuration file that contains a list of git project with JSON format,
                                "MY-SRC" -> git project source 
                                "MY-DST" -> git project's local directory
                                "MY-TAG" -> git project's tag name
                                "BRANCH" -> git project's branch name
    -c|--clone                : clone all dependent projects. If project exists, do nothing.
    -p|--pull                 : pull all existing projects
    -b|--branch <branch_name> : Check out to the corresponding "branch_name" branch, and update mb git configruation file.
                                If "branch_name" is not defined, it will checkout to the corresponding branch, which is 
                                defined in mb git configuration file. If the "BRANCH" scalar is not defined in configuration
                                file, do nothing.
    -t|--tag    <tag_name>    : Check out to the corresponding "tag_name" tag, and update mb git configruation file.
                                If "tag_name" is not defined, it will checkout to the corresponding tag, which is defined in 
                                mb git configuration file. If the "MY-TAG" scalar is not defined in configuration file, do 
                                nothing.
    -g|--git    <command>     : run the given Git command <command> on all sub-modules which is defined in mb git configuration 
                                file.
    -u|--update               : Search current git project commit number and update it to mb git file.
EOF
    exit;
}

my $options_number = scalar(@ARGV);

my $opt_success = GetOptions(
  'h|help!'    => \$opt_help,
  'f|file=s'   => \@git_cfg_file_list,
  'c|clone!'   => \$opt_git_clone,
  'p|pull!'    => \$opt_git_pull,
  'b|branch:s' => \$git_branch_name,
  't|tag:s'    => \$git_tag_name,
  'g|git=s'    => \$git_options,
  'u|update!'  => \$opt_git_update,
) or usage();
if($opt_help || $options_number==0) {usage();}
#print "git_branch_name = $git_branch_name\n";
my $json         = JSON->new->utf8;
my $hostname     = `hostname`;
chomp($hostname);
#my $git_server   = "192.168.8.8";
my $git_server   = "star-git";
my $user         = "git";

if(scalar(@git_cfg_file_list) == 0) {
  push @git_cfg_file_list, "star_git.cfg";
}

my $json_text;
my $perl_scalar;
my %dst_full_path;
chomp($user);
foreach my $git_cfg_file (@git_cfg_file_list) {
  open (my $fh, '<', $git_cfg_file) or die "Can't open $git_cfg_file:$!";
  local $/;
  $json_text = <$fh>;
  $perl_scalar = $json->decode($json_text);
  close ($fh);
  
  if($hostname ne 'shrd2') {
    print color ('bold yellow on_black');
    print "WARNING: Please switch $hostname to shrd2 server to use GIT server\n";
    print color ('reset clear');
  }
  
  foreach my $keys (keys %{$perl_scalar}) {
    print color('bold green on_black');
    printf("\n[%-s]:", $keys);
    print color('reset');
    print color('clear');
    
    #foreach my $git_cfg_element (keys %{${$perl_scalar}{$keys}}) {
    #  if(exists ${$perl_scalar}{$keys}{$git_cfg_element}) {
    #    printf (" [\"%s\"= %-s]", $git_cfg_element, ${$perl_scalar}{$keys}{$git_cfg_element});
    #    print color('reset');
    #    print color('clear');
    #  }
    #}
    print "\n";
  
    if(exists ${$perl_scalar}{$keys}{"MY-DST"}) {
      $dst_full_path{$keys} = &swap_env_path(${$perl_scalar}{$keys}{"MY-DST"});
      #print "$keys-[MY-DST]: $dst_full_path{$keys}\n";
    } else {
      die " \"MY-DST\" of $keys must be set in the git configuration file!";
    }
    if($opt_git_clone) {
      if(-d "$dst_full_path{$keys}/.git") {
        #if(-d ${$perl_scalar}{$keys}{"MY-DST"}) {
        print color('bold yellow on_black');
        print "  $dst_full_path{$keys} git project already exists!!!";
        print color('reset');
        &star_git_fetch($keys);
      } else {
        &star_git_clone($keys);
      }
      if(exists ${$perl_scalar}{$keys}{"MY-TAG"}) {
        &star_git_checkout_tag($keys, "Tag_No_Change");
      } else {
        if(exists ${$perl_scalar}{$keys}{"BRANCH"}) {
          &star_git_checkout_branch($keys);
        }
      }
    }
    if($opt_git_pull) {
      if(exists ${$perl_scalar}{$keys}{"BRANCH"}) {
        #&star_git_checkout_branch($keys);
        &star_git_pull($keys);
      }
    }
    if($git_options ne "") {
      my $git_cmd = sprintf("cd %-s; git %-s;", $dst_full_path{$keys}, $git_options);
      print $git_cmd."\n";
      my $return_value = system($git_cmd);
      if($return_value != 0) {
        print color('bold red on_black');
        print "ERROR: ";
        print color('bold green on_black');
        print "$git_cmd";
        print color('blink bold red on_black');
        print " FAILED!!!\n";
        print color('reset');
        print color('clear');
        exit(1);
      }
    }
    if($git_branch_name ne "DeFaulT") {
      if($git_branch_name ne "") {
        ${$perl_scalar}{$keys}{"BRANCH"} = $git_branch_name;
        $cfg_file_update_flag = 1;
      }
      &star_git_fetch($keys);
      &star_git_checkout_branch($keys);
    }
    if($git_tag_name ne "DeFaulT") {
      my $my_last_tag = "";
      if($git_tag_name ne "") {
        if(exists ${$perl_scalar}{$keys}{"MY-TAG"}) {
          $my_last_tag = ${$perl_scalar}{$keys}{"MY-TAG"};
        }
        ${$perl_scalar}{$keys}{"MY-TAG"} = $git_tag_name;
        $cfg_file_update_flag = 1;
      }
      &star_git_fetch($keys);
      &star_git_checkout_tag($keys, $my_last_tag);
    }
    if($opt_git_update) {
      my $current_commit_name = `cd $dst_full_path{$keys}; git log -n 1;`;
      if($current_commit_name =~ /^commit\s+(\S+)/) {
        $current_commit_name = $1;
      }
      ${$perl_scalar}{$keys}{"MY-TAG"} = $current_commit_name;
      $cfg_file_update_flag = 1;
    }
  
  }
  
  if($cfg_file_update_flag) {
    &star_cfg_file_update($git_cfg_file);
  }
}

sub star_git_pull {
  my $keys    = shift @_;
  my $git_cmd = sprintf("cd %-s; git pull;", $dst_full_path{$keys});
  print $git_cmd."\n";
  my $return_value = system($git_cmd);
  if($return_value != 0) {
    print color('bold red on_black');
    print "ERROR: ";
    print color('bold green on_black');
    print "$git_cmd";
    print color('blink bold red on_black');
    print " FAILED!!!\n";
    print color('reset');
    print color('clear');
    exit(1);
  }
}


sub star_git_clone {
  my $keys    = shift @_;
  my $git_cmd = sprintf("git clone %-s@%-s:%-s %-s",
                              $user, 
                              $git_server, 
                              ${$perl_scalar}{$keys}{"MY-SRC"},
                              $dst_full_path{$keys}
                             );
  print $git_cmd."\n";
  my $return_value = system($git_cmd);
  if($return_value != 0) {
    print color('bold red on_black');
    print "ERROR: ";
    print color('bold green on_black');
    print "$git_cmd";
    print color('blink bold red on_black');
    print " FAILED!!!\n";
    print color('reset');
    print color('clear');
    exit(1);
  }
}

sub star_git_fetch {
  my $keys    = shift @_;
  my $git_cmd = sprintf("cd %-s; git fetch;", $dst_full_path{$keys});
  my $return_value = system($git_cmd);
  if($return_value != 0) {
    print color('bold red on_black');
    print "ERROR: ";
    print color('bold green on_black');
    print "$git_cmd";
    print color('blink bold red on_black');
    print " FAILED!!!\n";
    print color('reset');
    print color('clear');
    exit(1);
  }
  $git_cmd = sprintf("cd %-s; git fetch --tag;", $dst_full_path{$keys});
  $return_value = system($git_cmd);
  if($return_value != 0) {
    print color('bold red on_black');
    print "ERROR: ";
    print color('bold green on_black');
    print "$git_cmd";
    print color('blink bold red on_black');
    print " FAILED!!!\n";
    print color('reset');
    print color('clear');
    exit(1);
  }
}


sub star_git_checkout_branch {
  my $keys    = shift @_;
  if(exists ${$perl_scalar}{$keys}{"BRANCH"}) {
    my $git_cmd = sprintf("cd %-s; git checkout %-s;", $dst_full_path{$keys}, ${$perl_scalar}{$keys}{"BRANCH"});
    my $return_value = system($git_cmd);
    if($return_value != 0) {
      print color('bold red on_black');
      print "ERROR: ";
      print color('bold green on_black');
      print "$git_cmd";
      print color('blink bold red on_black');
      print " FAILED!!!\n";
      print color('reset');
      print color('clear');
      exit(1);
    }
  }
}

sub star_git_checkout_tag {
  my $keys     = shift @_;
  my $last_tag = shift @_;
  if(exists ${$perl_scalar}{$keys}{"MY-TAG"}) {
    my $git_cmd = sprintf("cd %-s; git checkout %-s;", $dst_full_path{$keys}, ${$perl_scalar}{$keys}{"MY-TAG"});
    print $git_cmd."\n";
    my $return_value = system($git_cmd);
    if($return_value != 0) {
      print color('bold red on_black');
      print "WARNING ";
      print color('bold green on_black');
      print "$git_cmd";
      print color('blink bold red on_black');
      print " FAILED!!!\n";
      print color('reset');
      print color('clear');
      if($last_tag eq "Tag_No_Change") {
        exit(1);
      } elsif ($last_tag ne "") {
        ${$perl_scalar}{$keys}{"MY-TAG"} = $last_tag;
      } else {
        delete ${$perl_scalar}{$keys}{"MY-TAG"};
      }
    }
  } else {
    print color ('bold yellow on_black');
    print "  WARNING: Can't find \"MY-TAG\" variable!!!\n";
    print color('reset');
    print color('clear');
  }
}

sub swap_env_path {
  my $remove_line = shift @_;
  if($remove_line =~ /^(.*)\$\{?(\w+)\}?(.*)$/) {
    my $line_head = $1;
    my $path_env  = $2;
    my $line_end  = $3;
    my @path_env_list = grep {$_ eq $path_env} (keys %ENV);
    #print"@path_env_list\n";
    if(scalar(@path_env_list)==0) {
      print color ('bold red on_black');
      print "ERROR: Can't find Environment variable";
      print color ('bold blue on_black');
      print " \$\{$path_env\}";
      print color ('bold red on_black');
      print "!!!\n";
      print color('reset clear');
      exit(1);
    } else {
      #print("Debug:\$\{$path_env\}->$ENV{$path_env}\n");
      $remove_line=$line_head.$ENV{$path_env}.$line_end;
    }
  }
  return $remove_line;
}

sub star_cfg_file_update {
  my $my_git_cfg_file = shift @_;
  open (my $fh, '>', $my_git_cfg_file) or die "Can't open $my_git_cfg_file:$!";
     $json = $json->sort_by($function_name);
  my $json_en_text = $json->pretty->encode($perl_scalar);
  print "$json_en_text\n";
  print $fh "$json_en_text\n";
  close($fh);
}

